//#include "scrambled_symbols.h"
#include "constants.h"
#include "gisusdb.hxx"
#include "gisusswfile.hxx"
#include "gisusswfileset.hxx"

#include "mytimer.hxx"
#include "priv/gisusdbimpl.hxx"
#include "mydb.hxx"

#include "myfilelog.hxx"
#include "loghelper.hxx"

#include "priv/dbcache.hxx"

#include <iostream>
#include <assert.h>
#include "task.hxx"
#include "user.hxx"
#include "keyword.hxx"
#include "utility.hxx"
#include "taskfactory.hxx"

using namespace std;

GisusDBImpl::GisusDBImpl(DB *db, FileLog *filelog)
  : _db(db), _filelog(filelog), _dbcache(0)
{
  //_dbcache=new DBCache();
}

GisusDBImpl::~GisusDBImpl()
{
  if (_dbcache)
    {
      //delete _dbcache;
      //_dbcache=0;
    }
}

bool GisusDBImpl::Connect()
{
  return _db->Connect();
}

bool GisusDBImpl::Disconnect()
{
  return _db->Disconnect();
}


User* GisusDBImpl::Login(const string &name, const string &pwd)
{
  DB::tQueryId qid;
  stringstream query;
  char **qdata;
  User* user = NULL;

  query<<"SELECT id, privilege, supervisor FROM user where name='"<<name<<"' AND pswd='"<<pwd<<"';";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      // Error!
      LOGERROR("GisusDBImpl::Login: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      // Here we can return without freeing any memory used by a query.
      return NULL;
    }

  if (qid==DB::SuccessButNoResult)
    {
      // No error but no file found!
      return NULL;
    }

  qdata=_db->FetchQuery(qid);
  
  if (qdata==0)
    {
      LOGERROR("GisusDBImpl::Login: FetchQuery returned NULL for query '"<<query.str()<<"'.");
      return NULL;
    }
  
  user = new User(atoi(qdata[0]), name, pwd, (User::UserPrivilege)atoi(qdata[1]), atoi(qdata[2]));

  if (_db->FetchQuery(qid)!=0)
    {
      LOGWARNING("GisusDBImpl::Login: FetchQuery returned more than one answer for '"<<query.str()<<"'.");
    }

  _db->FreeQuery(qid);

  return user; 
}
int GisusDBImpl::SaveTask(const Task* task)
{
	DB::tQueryId qid;
	stringstream query;

	string encodedTaskInfo;
	Utility::Encode(task->ToString(), encodedTaskInfo);

	query<<"INSERT INTO task(taskinfo, cjh, owner, type)"
		<<" values( '"<<encodedTaskInfo<<"','"<<task->cjh<<"', "<<task->Owner->Supervisor<<", "<<task->Type<<");";

	qid=_db->Query(query.str());

	assert(_db->AffectedRows() == 1);

   if (qid==DB::Undefined)
    {
      // Error!
      LOGERROR("GisusDBImpl::SaveTask: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      // Here we can return without freeing any memory used by a query.
      return 0;
    }

  return _db->LastInsertId();
}
bool GisusDBImpl::UpdateTask(const Task* task)
{
  DB::tQueryId qid;
  stringstream query;

	string encodedTaskInfo;
	Utility::Encode(task->ToString(), encodedTaskInfo);

  query<<"UPDATE task set taskInfo='"<<encodedTaskInfo<<"', cjh='"<<task->cjh<<"', status="<<task->Status;
  if (task->Id>0)
	  query<<"  where type="<<Task::YU_YUE_KAO_SHI<<" and id="<<task->Id;
  else
	  query<<"  where type="<<Task::YU_YUE_KAO_SHI<<" and cjh='"<<task->cjh<<"'";
  query<<" and Status<>"<<Task::SUCCEEDED<<";";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      // Error!
      LOGERROR("GisusDBImpl::UpdateTask: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      // Here we can return without freeing any memory used by a query.
      return false;
    }

  return true;
}

bool GisusDBImpl::MarkStatus(const Task* task, bool isZombie)
{
  DB::tQueryId qid;
  stringstream query;
  string table = "task";

  if (isZombie)
	  table = "zombie";
  query<<"UPDATE "<<table<<" set status="<<task->Status<<" where id="<<task->Id<<";";

  qid=_db->Query(query.str());
  
  //assert(_db->AffectedRows() == 1);
  
  if (qid==DB::Undefined)
    {
      // Error!
      LOGERROR("GisusDBImpl::UpdateTask: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      // Here we can return without freeing any memory used by a query.
      return false;
    }

  return true;
}

int GisusDBImpl::NewZombie(const Task* task)
{
	DB::tQueryId qid;
	stringstream query;

	query<<"INSERT INTO zombie(cjh) values( '"<<task->cjh<<"');";

	qid=_db->Query(query.str());

	assert(_db->AffectedRows() == 1);

   if (qid==DB::Undefined)
    {
      // Error!
      LOGERROR("GisusDBImpl::NewZombie: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      // Here we can return without freeing any memory used by a query.
      return 0;
    }

  return _db->LastInsertId();
}
std::vector<Task*> GisusDBImpl::GetZSeeds(Task::TaskStatus status)
{
  std::vector<Task*> seeds;
  DB::tQueryId qid;
  stringstream query;
  char **qdata;
  bool res = true;

  assert(status == Task::ZOMBIE_SEED || status == Task::HOT_SEED);

  query<<"SELECT id, taskinfo, cjh, result, status, type, owner FROM task"
		<<"  where type="<<Task::YU_YUE_KAO_SHI<<" and status="<<status<<";";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      LOGERROR("GisusDBImpl::GetZSeeds: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      return seeds;
    }

  if (qid==DB::SuccessButNoResult)
    {
      return seeds;
    }

  while (0 != (qdata=_db->FetchQuery(qid)))
  {
	  string decodedTaskInfo;
	  Utility::Decode(qdata[1], decodedTaskInfo);

	  Task* task= TaskFactory::CreateTask(atoi(qdata[5]), NULL, atoi(qdata[0]), decodedTaskInfo, (Task::TaskStatus)atoi(qdata[4]));
	  task->cjh = qdata[2];

	  seeds.push_back(task);
  }
  
  _db->FreeQuery(qid);

  return seeds;
}
std::vector<Task*> GisusDBImpl::LoadZombies(const Task* seed, int num)
{
  std::vector<Task*> tasks;
  DB::tQueryId qid;
  stringstream query;
  char **qdata;
  bool res = true;

  const string cjhRoot = seed->cjh.substr(0, 12);

  query<<"SELECT id, cjh FROM zombie where cjh like '%"<<cjhRoot<<"%'"
	  <<" and status="<<Task::CREATED<<" order by id";
  if (num)
  query<<" limit 0,"<<num<<";";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      LOGERROR("GisusDBImpl::LoadZombies: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      return tasks;
    }

  if (qid==DB::SuccessButNoResult)
    {
      return tasks;
    }

  Task* zombie = NULL;
  while (0 != (qdata=_db->FetchQuery(qid)))
  {
	  Task* zombie/* = new Task(*seed)*/;
	  zombie->Id = atoi(qdata[0]);
	  zombie->cjh = qdata[1];
	  tasks.push_back(zombie);
  }  
  _db->FreeQuery(qid);
  if (zombie == NULL)
	  return tasks;

  // Mark the zombies loaded.
  query<<"update zombie set status=2 where status=1 and id<"<<zombie->Id<<"+1 and cjh like  '%"<<seed->cjh<<"%'";

  qid=_db->Query(query.str());

  assert(qid==DB::SuccessButNoResult);
  assert((int)tasks.size() == num);

  _db->FreeQuery(qid);

  return tasks;
}
bool GisusDBImpl::UpdateZombie(const Task* task)
{
  DB::tQueryId qid;
  stringstream query;

  query<<"UPDATE zombie set cjh='"<<task->cjh<<"', status="<<task->Status<<"  where and id="<<task->Id<<";";

  qid=_db->Query(query.str());
  
  // For maybe no change.
  //assert(_db->AffectedRows() == 1);
  
  if (qid==DB::Undefined)
    {
      // Error!
      LOGERROR("GisusDBImpl::UpdateZombie: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      // Here we can return without freeing any memory used by a query.
      return false;
    }

  return (_db->AffectedRows() == 1);
}

Task* GisusDBImpl::GetTaskByCjh(const string& cjh, const User* user)
{
  Task* task = NULL;
  DB::tQueryId qid;
  stringstream query;
  char **qdata;
  bool res = true;

  query<<"SELECT task.id, taskinfo, cjh, result, status, type, owner FROM task"
	  <<"  where type="<<Task::YU_YUE_KAO_SHI<<" and status<>"<<Task::ZOMBIE_SEED<<" and status<>"<<Task::HOT_SEED<<" and status<>"<<Task::DELETED<<" and cjh like '%"<<cjh<<"%'";
  if (user) query<<" and owner="<<user->Supervisor;
  query<<" limit 0, 1;";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      LOGERROR("GisusDBImpl::GetTaskByCjh: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      return NULL;
    }

  if (qid==DB::SuccessButNoResult)
    {
      return NULL;
    }

  if (0 != (qdata=_db->FetchQuery(qid)))
  {
	  string decodedTaskInfo;
	  Utility::Decode(qdata[1], decodedTaskInfo);
	  task = TaskFactory::CreateTask(atoi(qdata[5]), user, atoi(qdata[0]), decodedTaskInfo, (Task::TaskStatus)atoi(qdata[4]));
	  //task->result = qdata[3]==NULL ? "" : qdata[3];
	  task->cjh = qdata[2];
  }

  if (0 != _db->FetchQuery(qid))
  {
	  LOGWARNING("GisusDBImpl::GetTaskByCjh: Query '"<<query.str()<<"' returned more than one result.");
  }

  _db->FreeQuery(qid);

  return task;
}
Task* GisusDBImpl::GetNeighbor(const User* user, int curTaskId, bool next)
{
  Task* task = NULL;
  DB::tQueryId qid;
  stringstream query;
  char **qdata;
  bool res = true;

  query<<"SELECT id, taskinfo, cjh, result, status, type FROM task"
	  <<" where type="<<Task::YU_YUE_KAO_SHI<<" and status<>"<<Task::ZOMBIE_SEED<<" and status<>"<<Task::HOT_SEED<<" and status<>"<<Task::DELETED;
  if (user) query<<" and owner="<<user->Supervisor;

  if (curTaskId>0)
  {
	  if (next)
		query<<" and id > "<<curTaskId<<" order by id asc limit 0, 1;";
	  else
		query<<" and id < "<<curTaskId<<" order by id desc limit 0, 1;";
  }
  else
  {
	  if (next)
		query<<" order by id desc limit 0, 1;";
	  else
		query<<" order by id asc limit 0, 1;";
  }

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      LOGERROR("GisusDBImpl::GetTaskByCjh: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      return NULL;
    }

  if (qid==DB::SuccessButNoResult)
    {
      return NULL;
    }

  if (0 != (qdata=_db->FetchQuery(qid)))
  {
	  string decodedTaskInfo;
	  Utility::Decode(qdata[1], decodedTaskInfo);

	  task = TaskFactory::CreateTask(atoi(qdata[5]), user, atoi(qdata[0]), decodedTaskInfo, (Task::TaskStatus)atoi(qdata[4]));
	  //task->result = qdata[3]==NULL ? "" : qdata[3];
	  task->cjh = qdata[2];
  }

  if (0 != _db->FetchQuery(qid))
  {
	  LOGWARNING("GisusDBImpl::GetTaskByCjh: Query '"<<query.str()<<"' returned more than one result.");
  }

  _db->FreeQuery(qid);

  return task;
}
std::vector<Task*> GisusDBImpl::GetYKTasks(void)
{
  std::vector<Task*> results;
  DB::tQueryId qid;
  stringstream query;
  char **qdata;

  query<<"SELECT task.id, taskinfo, cjh, result, status, type "
	  <<" FROM task"
	  <<" where type="<<Task::YU_YUE_KAO_SHI<<" and (status="<<Task::CREATED<<" or status="<<Task::LOADED<<");";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      LOGERROR("GisusDBImpl::GetYKTasks: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      return results;
    }

  if (qid==DB::SuccessButNoResult)
    {
      return results;
    }

  while (0 != (qdata=_db->FetchQuery(qid)))
  {
	  string decodedTaskInfo;
	  Utility::Decode(qdata[1], decodedTaskInfo);

	  Task* task;
	  task = TaskFactory::CreateTask(atoi(qdata[5]), NULL, atoi(qdata[0]), decodedTaskInfo, (Task::TaskStatus)atoi(qdata[4]));
	  task->cjh = qdata[2];
	  results.push_back(task);
  }

  _db->FreeQuery(qid);

  return results;
}
std::vector<Task*> GisusDBImpl::GetPTasks(const User* user, Task::TaskStatus status)
{
  std::vector<Task*> results;
  DB::tQueryId qid;
  stringstream query;
  char **qdata;

  query<<"SELECT task.id, taskinfo, cjh, result, status, type, user.id, user.name, user.pswd, user.privilege, user.supervisor"
	  <<" FROM task JOIN user ON user.id = task.owner"
	  <<"  where type="<<Task::YU_YUE_KAO_SHI<<" and status="<<status
	  <<" and (owner="<<user->Supervisor<<" or owner in (select id from user where privilege="<<User::ADMIN<<"));";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      LOGERROR("GisusDBImpl::GetTaskByCjh: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      return results;
    }

  if (qid==DB::SuccessButNoResult)
    {
      return results;
    }

  while (0 != (qdata=_db->FetchQuery(qid)))
  {
	  string decodedTaskInfo;
	  Utility::Decode(qdata[1], decodedTaskInfo);

	   // User.
	  const User* myuser = (user->Id==atoi(qdata[6])) ? user : new User(atoi(qdata[6]), qdata[7], qdata[8], (User::UserPrivilege)atoi(qdata[9]), atoi(qdata[10]));

	  Task* task;
	  task = TaskFactory::CreateTask(atoi(qdata[5]), myuser, atoi(qdata[0]), decodedTaskInfo, (Task::TaskStatus)atoi(qdata[4]));
	  //task->result = qdata[3]==NULL ? "" : qdata[3];
	  task->cjh = qdata[2];
	  results.push_back(task);
  }

  _db->FreeQuery(qid);

  return results;
}
bool GisusDBImpl::DeleteTask(const string& cjh)
{
  DB::tQueryId qid;
  stringstream query;

  query<<"delete from task where type="<<Task::YU_YUE_KAO_SHI<<" and cjh='"<<cjh<<"' and status<>"<<Task::SUCCEEDED<<" and status<>"<<Task::LOADED<<";";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      LOGERROR("GisusDBImpl::DeleteTask: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      return false;
    }
  if (_db->AffectedRows() > 1)
  {
      LOGWARNING("GisusDBImpl::DeleteTask: Query '"<<query.str()<<"' AffectedRows > 1.");
  }
  
  return _db->AffectedRows() > 0;
}
bool GisusDBImpl::RestoreBEndData(const User* user)
{
  DB::tQueryId qid;
  stringstream query;

  query<<"update task set status=1 where (status="<<Task::LOADED<<" or status="<<Task::CON_INUSE<<" or status="<<Task::FROZEN<<") and owner="<<user->Supervisor<<";";

  qid=_db->Query(query.str());
  
  if (qid==DB::Undefined)
    {
      LOGERROR("GisusDBImpl::RestoreBEndData: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
      return false;
    }
  if (_db->AffectedRows() == 0)
  {
      LOGWARNING("GisusDBImpl::RestoreBEndData: Query '"<<query.str()<<"' AffectedRows == 0.");
  }

  // Admin can reset all zombies as well.
  if (user->Privilege > User::SUPERVISOR)
  {
	  query.str("");
	  query<<"update zombie set status=1 where status=2;";

	  qid=_db->Query(query.str());
	  
	  if (qid==DB::Undefined)
		{
		  LOGERROR("GisusDBImpl::RestoreBEndData: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
		  return false;
		}
	  if (_db->AffectedRows() == 0)
	  {
		  LOGWARNING("GisusDBImpl::RestoreBEndData: Query '"<<query.str()<<"' AffectedRows == 0.");
	  }
  }

  return true;
}
bool GisusDBImpl::LockDBTable(const string& table)
{
	DB::tQueryId qid;
	stringstream query;
	query<<"LOCK TABLES "<<table<<";";
	qid=_db->Query(query.str());
	if (qid==DB::Undefined)
	{
	  // Error!
	  LOGERROR("GisusDBImpl::UnlockDBTables: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
	  return false;
	}
	return true;
}
bool GisusDBImpl::UnlockDBTables(void)
{
	DB::tQueryId qid;
	stringstream query;
	query<<"UNLOCK TABLES;";
	qid=_db->Query(query.str());
	if (qid==DB::Undefined)
	{
	  // Error!
	  LOGERROR("GisusDBImpl::UnlockDBTables: Query '"<<query.str()<<"' returned Error Code "<<_db->LastErrorCode()<<": '"<<_db->LastErrorMsg()<<"'.");
	  return false;
	}
	return true;
}
///////////////////////////////////////////////////////////////////////////////
//
//////////////////////

DBTableLock::DBTableLock(GisusDB* gisusdb1, const string& tablename, FileLog* filelog)
:gisusdb(gisusdb1),
_filelog(filelog)
{
  gisusdb->LockDBTable(tablename);
}
DBTableLock::~DBTableLock()
{
	gisusdb->UnlockDBTables();
}
GisusDB::GisusDB(DB *db, FileLog *filelog)
  : dbInUse(),
  _impl(0)
{
  _impl = new GisusDBImpl(db, filelog);
}

GisusDB::GisusDB(const GisusDB &model)
{
#if !defined(RELEASE)
  // This constructor should never be used!
  cerr<<"GisusDB::GisusDB(const GisusDB &model): This function is not supposed to be called! Crashing."<<endl; cerr.flush();
  assert(0);
#endif
}


GisusDB::~GisusDB()
{
  if (_impl) delete _impl;
  _impl=0;
}

bool GisusDB::Connect()
{
Utility::Lock lock(&dbInUse);
  return _impl->Connect();
}

bool GisusDB::Disconnect()
{
Utility::Lock lock(&dbInUse);
  return _impl->Disconnect();
}

User* GisusDB::Login(const string &user, const string &pwd)
{
Utility::Lock lock(&dbInUse);
  return _impl->Login(user,pwd);
}
int GisusDB::SaveTask(const Task* task)
{
  Lock lock(&dbInUse);
  return _impl->SaveTask(task);
}
bool GisusDB::UpdateTask(const Task* task)
{
Utility::Lock lock(&dbInUse);
  return _impl->UpdateTask(task);
}
bool GisusDB::MarkStatus(const Task* task, bool isZombie)
{
Utility::Lock lock(&dbInUse);
  return _impl->MarkStatus(task, isZombie);
}
int GisusDB::NewZombie(const Task* task)
{
Utility::Lock lock(&dbInUse);
  return _impl->NewZombie(task);
}
std::vector<Task*> GisusDB::GetZSeeds(Task::TaskStatus status)
{
Utility::Lock lock(&dbInUse);
std::vector<Task*> seeds = _impl->GetZSeeds(status);
std::vector<Task*>::iterator iter = seeds.begin();
while (iter != seeds.end())
{
	(*iter)->SetGisusDB(this);
	++iter;
}
return seeds;
}
std::vector<Task*> GisusDB::LoadZombies(const Task* seed, int num)
{
	std::vector<Task*> results;

	Utility::Lock lock(&dbInUse);
	results = _impl->LoadZombies(seed, num);;
	
	std::vector<Task*>::iterator iterzs = results.begin();
	while (iterzs != results.end())
	{
		(*iterzs)->SetGisusDB(this);
		++iterzs;
	}
	return results;
}
bool GisusDB::UpdateZombie(const Task* task)
{
Utility::Lock lock(&dbInUse);
  return _impl->UpdateZombie(task);
}
Task* GisusDB::GetTaskByCjh(const string& cjh, const User* user)
{
	Utility::Lock lock(&dbInUse);
	Task* task = _impl->GetTaskByCjh(cjh, user);
	if (task)
	{
		task->SetGisusDB(this);
	}
	return task;
}
Task* GisusDB::GetNeighbor(const User* user, int curTaskId, bool next)
{
	Utility::Lock lock(&dbInUse);
	Task* task = _impl->GetNeighbor(user, curTaskId, next);
	if (task)
	{
		task->SetGisusDB(this);
	}
	return task;
}
std::vector<Task*> GisusDB::GetPTasks(const User* user, Task::TaskStatus status)
{
	Utility::Lock lock(&dbInUse);
	std::vector<Task*> results = _impl->GetPTasks(user, status);
	std::vector<Task*>::iterator iter = results.begin();
	while (iter != results.end())
	{
		(*iter)->SetGisusDB(this);
		++iter;
	}
	return results;
}
bool GisusDB::DeleteTask(const string& cjh)
{
	Utility::Lock lock(&dbInUse);
	return _impl->DeleteTask(cjh);
}
bool GisusDB::RestoreBEndData(const User* user)
{
	Utility::Lock lock(&dbInUse);
	return _impl->RestoreBEndData(user);
}
bool GisusDB::LockDBTable(const string& table)
{
	Utility::Lock lock(&dbInUse);
	return _impl->LockDBTable(table);
}
bool GisusDB::UnlockDBTables(void)
{
	Utility::Lock lock(&dbInUse);
	return _impl->UnlockDBTables();
}

std::vector<Task*> GisusDB::GetYKTasks(void)
{
	Utility::Lock lock(&dbInUse);
	std::vector<Task*> results = _impl->GetYKTasks();
	std::vector<Task*>::iterator iter = results.begin();
	while (iter != results.end())
	{
		(*iter)->SetGisusDB(this);
		++iter;
	}

	return results;
}